package com.gitee.l0km.beanfilter.core;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.gitee.l0km.beanfilter.core.utils.CaseSupport.TO_CAMEL_CASE_IF_SNAKE;
import static com.gitee.l0km.beanfilter.core.utils.CaseSupport.isSnakecase;
import static com.gitee.l0km.beanfilter.core.utils.CaseSupport.toCamelcase;
import static com.google.common.base.MoreObjects.firstNonNull;

import java.util.Objects;
import java.util.Set;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;

/**
 * 字段过滤简单实现基类
 * @author guyadong
 *
 */
public class BaseFieldFilter implements IFieldFilter {
	static final String[] EMPTY_STRING_ARRAY = new String[0];
	static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
	private final Class<?> beanClass;
	private final boolean whiteList;
	private final Set<String> excludeNames;
	private final Set<String> includeNames;
	/** 是否在camel-case和snake-case之间自动转换 */
	protected final boolean autoCase;
	protected final ActivationConditions activeOn;

	/**
	 * @param beanClass Java Bean类型
	 * @param autoCase 是否将snake-case和camel-case格式的字段字视为同一字段，如firstName和first_name,为{@code true}时视为同一个名字。
	 * @param whiteList 为{@code true}时为白名单模式，{@code filterNames}指定字段名被允许输入/输出，否则为黑名单模式，{@code filterNames}指定字段名被禁止输入/输出
	 * @param filterNames 过滤的字段名列表
	 * @param activeOnClasses 过滤器激活器类型列表，必须为{@link Activation}接口实现
	 * @param activeOnClassNames  过滤器激活器类名列表，必须为{@link Activation}接口实现类名,与{@code activeOnClasses}参数作用相同，用字符器定义类名在特定场景可以减少依赖
	 * @param and 指定多个过滤器激活器时，激活判断模式，为{@code true}为AND模式，所有激活器条件都匹配才能激活过滤器，否则为OR模式，任意一激活器条件都匹配就可以激活过滤器
	 */
	public BaseFieldFilter(Class<?> beanClass, boolean autoCase,boolean whiteList,String[] filterNames,Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean and) {
		this(beanClass, 
				autoCase,
				whiteList, 
				whiteList ? EMPTY_STRING_ARRAY : filterNames,
				whiteList ?  filterNames: EMPTY_STRING_ARRAY,
				activeOnClasses, activeOnClassNames, and);
	}
	protected BaseFieldFilter(Class<?> beanClass, boolean autoCase,boolean whiteList,String[] excludeNames,
			String[] includeNames, Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean and) {
		this.beanClass = checkNotNull(beanClass,"beanClass is null");
		this.autoCase = autoCase;
		this.whiteList = whiteList;
		this.excludeNames = ImmutableSet.copyOf(normalize(firstNonNull(excludeNames,EMPTY_STRING_ARRAY), autoCase));
		this.includeNames = ImmutableSet.copyOf(normalize(firstNonNull(includeNames,EMPTY_STRING_ARRAY), autoCase));
		this.activeOn = new ActivationConditions(activeOnClasses, activeOnClassNames, and);
	}
	/**
	 * @return beanClass
	 */
	public Class<?> getBeanClass() {
		return beanClass;
	}
	public boolean isWhiteList() {
		return whiteList;
	}
	public Set<String> getExcludeNames() {
		return excludeNames;
	}
	public Set<String> getIncludeNames() {
		return includeNames;
	}
	public boolean isActive(Class<?> beanClass) {
		return activeOn.isActive(this, beanClass);
	}
	private static Iterable<String> normalize(String[]input, boolean autoCase) {
		FluentIterable<String> filtered = FluentIterable.from(input).filter(Predicates.notNull());
		if(autoCase) {
			/** 将所有字段名转为camel-case命名格式 */
			return filtered.transform(TO_CAMEL_CASE_IF_SNAKE);
		}
		return filtered;
	}
	@Override
	public boolean permit(Class<?> clazz, String fieldName) {
		return permit(clazz,fieldName,autoCase);
	}
	protected boolean permit(Class<?> clazz, String fieldName,boolean autoCase) {
		if(isActive(clazz)) {
			return doPermit(clazz, fieldName, autoCase, whiteList);
		}
		return true;
	}
	
	protected boolean doPermit(Class<?> clazz, String fieldName,boolean autoCase, boolean whiteList) {
		if(null != clazz && null != fieldName && getBeanClass().isAssignableFrom(clazz)) {
			return permitName(fieldName, autoCase, whiteList, getExcludeNames(), getIncludeNames());
		}
		return true;
	}
	protected boolean permitName(String fieldName, boolean autoCase,boolean whiteList,Set<String> excludeNames, Set<String> includeNames) {
		if(autoCase && isSnakecase(fieldName)) {
			/** 将字段名转为camel-case命名格式 */
			fieldName = toCamelcase(fieldName);
		}
		if(whiteList) {
			return includeNames.contains(fieldName);
		}else {
			return !excludeNames.contains(fieldName);
		}
	}
	@Override
	public int hashCode() {
		return Objects.hash(activeOn, autoCase, beanClass, excludeNames, includeNames, whiteList);
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BaseFieldFilter other = (BaseFieldFilter) obj;
		return Objects.equals(activeOn, other.activeOn) && autoCase == other.autoCase
				&& Objects.equals(beanClass, other.beanClass) && Objects.equals(excludeNames, other.excludeNames)
				&& Objects.equals(includeNames, other.includeNames) && whiteList == other.whiteList;
	}
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("BaseFieldFilter [beanClass=").append(beanClass).append(", whiteList=").append(whiteList)
				.append(", excludeNames=").append(excludeNames).append(", includeNames=").append(includeNames)
				.append(", autoCase=").append(autoCase).append(", activeOn=").append(activeOn).append("]");
		return builder.toString();
	}
}
